﻿#include <iostream>
#include <string>
#include <assert.h>
/* glad必须先包含，后包含glfw */
#include "glad/glad.h"
#include "glfw/glfw3.h"
#include "checkError.h"
#include "Application.h"

// 声明且实现窗体变化回调
void OnResze(int width, int height)
{
  std::cout << "窗体最新大小：" << width << " " << height << std::endl;
  GL_CALL(glViewport(0, 0, width, height));
}

void OnKey(int key, int scancode,  int action, int mods)
{
  std::cout << "key：" << key << std::endl;
}


void prepareSingleBuffer()
{
  float positions[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f
  };

  float colors[] = {
    1.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
    0.0f,  0.0f, 1.0f
  };
  // 1 生成一个vbo
  GLuint vboPos = 0, vboColor = 0;
  GL_CALL(glGenBuffers(1, &vboPos));
  GL_CALL(glGenBuffers(1, &vboColor));

  // 2 绑定当前vbo，到opengl状态机的当前vbo插槽
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboPos));
  GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW));

  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboColor));
  GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW));

  // 创建及绑定vao
  GLuint vaoPos = 0;
  GL_CALL(glGenVertexArrays(1, &vaoPos));
  GL_CALL(glBindVertexArray(vaoPos));
  // 描述为指数型
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboPos)); // 只有绑定了vbo，下面的属性描述才会有当前vbo有关系
  GL_CALL(glEnableVertexAttribArray(0));
  GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (GLvoid*)0));

  // 创建及绑定vao
  GLuint vaoColor = 0;
  GL_CALL(glGenVertexArrays(1, &vaoColor));
  GL_CALL(glBindVertexArray(vaoColor));
  // 描述为指数型
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboColor)); // 只有绑定了vbo，下面的属性描述才会有当前vbo有关系
  GL_CALL(glEnableVertexAttribArray(1));
  GL_CALL(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (GLvoid*)0));

  GL_CALL(glBindVertexArray(0));
}

void prepareInterleavedBuffer()
{
  float vertices[] = {
    -0.5f, -0.5f, 0.0f,1.0f, 0.0f, 0.0f,
     0.5f, -0.5f, 0.0f,0.0f, 1.0f, 0.0f,
     0.0f,  0.5f, 0.0f,0.0f,  0.0f, 1.0f
  };

  GLuint vbo = 0;
  GL_CALL(glGenBuffers(1, &vbo));
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
  GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));

  GLuint vao = 0;
  GL_CALL(glGenVertexArrays(1, &vao));
  GL_CALL(glBindVertexArray(vao));

  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));

  GL_CALL(glEnableVertexAttribArray(0));
  GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (void*)0));

  GL_CALL(glEnableVertexAttribArray(1));
  GL_CALL(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (void*)(3 * sizeof(GL_FLOAT))));

  GL_CALL(glBindVertexArray(0));
}

void prepareShader()
{
  const char* vertexShader =
    "#version 460 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "void main()\n"
    "{\n"
    " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
    "}\0";

  const char* fragmentShader =
    "#version 460 core\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "{\n"
    " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
    "}\0";

  GLuint vertex, fragment;
  vertex = glCreateShader(GL_VERTEX_SHADER);
  fragment = glCreateShader(GL_FRAGMENT_SHADER);

  glShaderSource(vertex, 1, &vertexShader, NULL);
  glShaderSource(fragment, 1, &fragmentShader, NULL);

  int success = 0;
  char infoLog[1024] = { 0 };
  glCompileShader(vertex);
  // 检查vertex编译结果
  glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
  if (!success)
  {
    glGetShaderInfoLog(vertex, 1024, NULL, infoLog);
    std::cout << "Error: VERTEX SHADER COMPILE ERROR: " << infoLog << std::endl;
  }
  glCompileShader(fragment);
  // 检查fragment编译结果
  glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
  if (!success)
  {
    glGetShaderInfoLog(fragment, 1024, NULL, infoLog);
    std::cout << "Error: FRAGMENT SHADER COMPILE ERROR: " << infoLog << std::endl;
  }

  // 创建Program
  GLuint prog = 0;
  prog = glCreateProgram();

  // 附着程序到program中
  glAttachShader(prog, vertex);
  glAttachShader(prog, fragment);

  // 链接程序
  glLinkProgram(prog);
  glGetProgramiv(prog, GL_LINK_STATUS, &success);
  if (!success)
  {
    glGetProgramInfoLog(prog, 1024, NULL, infoLog);
    std::cout << "Error: PROGRAM LINK ERROR: " << infoLog << std::endl;
  }

  glDeleteShader(vertex);
  glDeleteShader(fragment);
}

int main()
{
  
  if (!app->init(800, 600))
  {
    return -1;
  }

  // 设置窗口回调
  app->setResizeCallback(OnResze);
  app->setKeyboardCallback(OnKey);

  /* 设置opengl视口以及清理颜色 */
  GL_CALL(glViewport(0, 0, 800, 600));
  GL_CALL(glClearColor(0.2f, 0.3f, 0.3f, 1.0f));

  prepareShader();
  prepareInterleavedBuffer();

  // 3 执行窗体循环
  while (app->update())
  {
    // 执行opengl画布清理操作
    glClear(GL_COLOR_BUFFER_BIT);
  }

  // 4 退出程序前清理
  app->destroy();
  return 0;
}